/**
 * Services to manipulate schedule jobs
 */
var monq = require('monq');
var config = require('../config');
var client = monq(config.db);
var logger = require('../common/loggerUtil').getLogger('scheduling');
var appInit = require('../appinit.js');
var worker = client.worker(['eventq']);
var CronJob = require('cron').CronJob;
var util = require('util');
var Schedule = require('../proxy').Schedule;
var Topic = require('../proxy').Topic;
var Database = require('./database');

appInit.add();

/**
 * all scheduling jobs are save in this object
 * @type {Object} object are in schedule._id: node-schedule format.
 */
var crons = {};

/**
 * create cron job
 */

function _createCronJob(data) {
    try {
        logger.debug('_createCronJob', '>> create cron job ... ' + data.time_settings);
        // crons[data._id] = new CronJob(data.time_settings, function() {
        //     logger.debug('this is a good job!');
        // }, null, true, 'Asia/Shanghai');

        /**
         * Use a more verbal description
         * @param  {[type]} )           {                                                             logger.debug('this is a good job!');                } [description]
         * @param  {[type]} onComplete: null            [description]
         * @param  {[type]} start:      true            [description]
         * @param  {[type]} timeZone:   'Asia/Shanghai' [description]
         * @param  {[type]} context:    data                                    } [description]
         * @return {[type]}             [description]
         */
        crons[data._id] = new CronJob({
            cronTime: data.time_settings,
            onTick: function() {
                var self = this;
                var schedule_tag = util.format('schedule.%d', self.schedule_id);
                logger.debug('context', JSON.stringify(self));
                Topic.newAndSave(self.name, self.description, self.category,
                    /**
                     * for _scheduleOnPut, author is the populated object,
                     * for _scheduleOnPost, author is the ObjectID
                     */
                    typeof(self.author) === 'object' && self.author._id ? self.author._id : self.author,
                    function(err, topic) {
                        if (err) {
                            logger.error(schedule_tag, err);
                        } else {
                            logger.debug(schedule_tag, 'new task is created. ' + JSON.stringify(topic));
                        }
                    });
            },
            onComplete: null,
            start: true,
            timeZone: 'Asia/Shanghai',
            // access by this.
            // Get example https://github.com/ncb000gt/node-cron/blob/master/tests/test-cron.js#L421
            context: data
        });
    } catch (ex) {
        logger.error("cron pattern not valid");
    }
}

/**
 * a new schedule is created.
 * @param  {[type]} payload [description]
 * @return {[type]}         [description]
 */
function _scheduleOnPost(payload) {
    var data = payload[0];
    logger.debug('_scheduleOnPost', data);
    if (crons[data._id]) {
        // crons[data._id].canceled();
        // delete crons[data._id];
        logger.error('_scheduleOnPost', 'Cron job alreay exist, do you mean an update ? Give up further operations.');
    } else {
        console.log(">> _scheduleOnPost create " + JSON.stringify(data));
        _createCronJob(data);
    }
}

/**
 * a schedule is updated.
 * @param  {[type]} payload [description]
 * @return {[type]}         [description]
 */
function _scheduleOnPut(payload) {
    logger.debug('_scheduleOnPut', payload);
    var data = payload[0];
    logger.debug('_scheduleOnPost', data);
    if (crons[data._id]) {
        crons[data._id].stop();
        delete crons[data._id];
        logger.debug('_scheduleOnPut', 'cancel old job successfully.');
    }
    _createCronJob(data);
}

/**
 * a schedule is deleted.
 * @param  {[type]} payload [description]
 * @return {[type]}         [description]
 */
function _scheduleOnDel(payload) {
    logger.debug('_scheduleOnDel', payload);
    var data = payload[0];
    if (crons[data._id]) {
        crons[data._id].stop();
        delete crons[data._id];
        logger.debug('_scheduleOnDel', 'Cancel old job successfully.');
    }
}

// handle arrival event relatively
function _handle(event) {
    switch (event.event) {
        case 'collection:schedule:post':
            _scheduleOnPost(JSON.parse(event.args));
            break;
        case 'collection:schedule:put':
            _scheduleOnPut(JSON.parse(event.args));
            break;
        case 'collection:schedule:del':
            _scheduleOnDel(JSON.parse(event.args));
            break;
        default:
            logger.warn('_handle', 'unrecognized event happens.');
            break;
    }
}


// Get monq sub/pub details.
// https://github.com/scttnlsn/monq
worker.register({
    schedule: function(params, callback) {
        try {
            // logger.debug('schedule', params);
            // event {date, event, args, _id} 
            logger.debug('event arrivals', JSON.stringify(params));
            _handle(params);
            // result passed to worker.on("completed")
            // the second parameter is data.result.
            callback(null, 'reversed');
        } catch (err) {
            // then, worker on failed event happens.
            callback(err);
        }
    }
});

worker.on('dequeued', function(data) {
    logger.debug('Dequeued', JSON.stringify(data));
});

worker.on('failed', function(data) {
    logger.debug('Failed', JSON.stringify(data));
});

worker.on('complete', function(data) {
    logger.debug('Complete', JSON.stringify(data));
});

worker.on('error', function(err) {
    logger.error('Error:');
    logger.error(err);
    worker.stop();
});

worker.start();


Database.initPromise.onFulfill(function() {
    try {
        logger.info('scheduling service is started.');
        Schedule.findAllSchedule()
            .then(function(docs) {
                docs.forEach(function(doc) {
                    console.log("id .." + JSON.stringify(doc));
                    // start repo jobs.
                    _createCronJob(doc);
                });
                /**
                 * eventq is started.
                 * if no error, resolve appInit.
                 */
                logger.info('eventq service is started.');
                appInit.resolve();
            })
            .fail(function(error) {
                logger.error(error);
                appInit.reject(error);
            });
    } catch (err) {
        logger.error(err);
        appInit.reject(err);
    }
});
